#ifndef __RANDOM_HPP__
#define __RANDOM_HPP__

#include <random>
#include <cstdint>
#include "Vector2.hpp"
#include "Vector3.hpp"

namespace PocketSurvival::Engine
{
    static std::random_device rd;

    class Random
    {
    private:
        int32_t m_seed;
        std::mt19937 m_random;

    public:
        Random();
        Random(int32_t seed);

        inline int32_t seed() noexcept
        {
            return m_seed;
        }

        inline int32_t getInt() noexcept
        {
            return m_random() & INT32_MAX;
        }
        inline int32_t getInt(int32_t bound)
        {
            return (int32_t)((int64_t)getInt() * (int64_t)bound / ((int64_t)INT32_MAX + 1));
        }
        inline int32_t getInt(int32_t min, int32_t max) noexcept
        {
            return (int32_t)(min + (int64_t)getInt() * (int64_t)(max - min + 1) / ((int64_t)INT32_MAX + 1));
        }

        inline float getFloat()
        {
            return getInt() / 2.147484E+09f;
        }
        inline float getFloat(float min, float max)
        {
            return min + getFloat() * (max - min);
        }

        inline int32_t getSign()
        {
            return getInt() % 2 * 2 - 1;
        }
        inline bool getBool()
        {
            return (getInt() & 0x01) != 0;
        }
        inline bool getBool(float probability)
        {
            return getFloat() < probability;
        }
		inline void seed(int32_t seed)
		{
			m_random.seed(seed);
		}

		inline Vector2 getVector2()
		{
			float num;
			float num2;
			float num3;
			float num4;
			float num5;
			do
			{
				num = 2.0f * getFloat() - 1.0f;
				num2 = 2.0f * getFloat() - 1.0f;
				num3 = num * num;
				num4 = num2 * num2;
				num5 = num3 + num4;
			}
			while (!(num5 < 1.0f));
			float num6 = 1.0f / num5;
			return Vector2((num3 - num4) * num6, 2.0f * num * num2 * num6);
		}
		inline Vector2 getVector2(float length)
		{
			return Vector2::Normalize(getVector2()) * length;
		}
		inline Vector2 getVector2(float minLength, float maxLength)
		{
			return Vector2::Normalize(getVector2()) * getFloat(minLength, maxLength);
		}

		inline Vector3 getVector3()
		{
			float num;
			float num2;
			float num3;
			do
			{
				num = 2.0f * getFloat() - 1.0f;
				num2 = 2.0f * getFloat() - 1.0f;
				num3 = num * num + num2 * num2;
			}
			while (!(num3 < 1.0f));
			float num4 = MathUtils::Sqrt<float>(1.0f - num3);
			return Vector3(2.0f * num * num4, 2.0f * num2 * num4, 1.0f - 2.0f * num3);
		}
		inline Vector3 getVector3(float length)
		{
			return Vector3::Normalize(getVector3()) * length;
		}
		inline Vector3 getVector3(float minLength, float maxLength)
		{
			return Vector3::Normalize(getVector3()) * getFloat(minLength, maxLength);
		}



    };

    inline Random::Random() : m_seed(rd()), m_random(m_seed) {}
    inline Random::Random(int32_t seed) : m_seed(seed), m_random(m_seed) {}
}

#endif